package com.example.tbplus14.utils.http;

import lombok.extern.slf4j.Slf4j;
import okhttp3.*;

import javax.net.ssl.*;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;

/**
 * @author XMF
 **/
@Slf4j
public class OkHttp {

    private static final MediaType JSON = MediaType.parse("application/json");

    private static final MediaType XML = MediaType.parse("application/xml");

    private static final OkHttpClient okHttpClient;

    private static final int CONNECT_TIMEOUT = 30;
    private static final int READ_TIMEOUT = 30;
    private static final int WRITE_TIMEOUT = 30;

    static {
        ConnectionPool pool = new ConnectionPool(200, 5, TimeUnit.MINUTES);
        okHttpClient =
                new OkHttpClient.Builder()
                        .retryOnConnectionFailure(false)
                        .connectionPool(pool)
                        .connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
                        .readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
                        .writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
                        .sslSocketFactory(createSSLSocketFactory(), Objects.requireNonNull(manager()))
                        .hostnameVerifier(new TrustAllHostnameVerifier())
                        .build();
    }

    private static X509TrustManager manager() {
        try {
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(
                    TrustManagerFactory.getDefaultAlgorithm());
            trustManagerFactory.init((KeyStore) null);
            TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
            if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
                throw new IllegalStateException("Unexpected default trust managers:"
                        + Arrays.toString(trustManagers));
            }
            return (X509TrustManager) trustManagers[0];
        } catch (Exception e) {
            log.error("OkHttpException", e);
        }
        return null;
    }

    private static SSLSocketFactory createSSLSocketFactory() {
        SSLSocketFactory ssfFactory = null;
        try {
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(null, new TrustManager[]{new TrustAllCerts()}, new SecureRandom());
            ssfFactory = sc.getSocketFactory();
        } catch (Exception e) {
            log.error("HttpFactoryException", e);
        }
        return ssfFactory;
    }

    private static class TrustAllCerts implements X509TrustManager {

        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new X509Certificate[0];
        }
    }

    private static class TrustAllHostnameVerifier implements HostnameVerifier {

        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    }

    public static String doPost(String url, Map<String, String> maps) {
        FormBody.Builder formBodyBuilder = new FormBody.Builder();
        Set<Entry<String, String>> entrySet = maps.entrySet();
        for (Entry<String, String> entry : entrySet) {
            formBodyBuilder.add(entry.getKey(), String.valueOf(entry.getValue()));
        }
        RequestBody body = formBodyBuilder.build();
        Request request = new Request.Builder().url(url).post(body).build();
        return execute(request);
    }

    public static String doPost(String url) {
        return doPost(url, Collections.emptyMap());
    }

    public static String doPostJson(String url, String json) {
        Request request =
                new Request.Builder()
                        .url(url)
                        .addHeader("Connection", "close")
                        .post(RequestBody.create(JSON, json))
                        .build();
        return execute(request);
    }

    public static String doPostJson(String url, String json, Map<String, String> headers) {
        Request.Builder post = new Request.Builder()
                .url(url)
                .addHeader("Connection", "close")
                .post(RequestBody.create(JSON, json));
        for (Entry<String, String> stringStringEntry : headers.entrySet()) {
            post.header(stringStringEntry.getKey(), stringStringEntry.getValue());
        }
        Request request = post.build();
        return execute(request);
    }

    public static Response doPostJsonResponse(String url, String json, Map<String, String> headers) {
        Request.Builder post = new Request.Builder()
                .url(url)
                .addHeader("Connection", "close")
                .post(RequestBody.create(JSON, json));
        for (Entry<String, String> stringStringEntry : headers.entrySet()) {
            post.header(stringStringEntry.getKey(), stringStringEntry.getValue());
        }
        Request request = post.build();
        return executeResponse(request);
    }

    public static String get(String url, Map<String, String> queries) {
        StringBuilder sb = new StringBuilder(url);
        if (queries != null && queries.keySet().size() > 0) {
            boolean firstFlag = true;
            for (Entry<String, String> entry : queries.entrySet()) {
                if (firstFlag) {
                    sb.append("?");
                    sb.append(entry.getKey());
                    sb.append("=");
                    sb.append(entry.getValue());
                    firstFlag = false;
                } else {
                    sb.append("&");
                    sb.append(entry.getKey());
                    sb.append("=");
                    sb.append(entry.getValue());
                }
            }
        }
        Request request = new Request.Builder().url(sb.toString()).addHeader("Connection", "close").build();
        return execute(request);
    }

    public static String get(String url) {
        return get(url, null);
    }

    public static String get(String url, String headerKey, String headerValue) {
        Request request =
                new Request.Builder().url(url).addHeader(headerKey, headerValue).build();
        return execute(request);
    }

    public static Response getResponse(String url, String headerKey, String headerValue) {
        Request request =
                new Request.Builder().url(url).addHeader(headerKey, headerValue).build();
        return executeResponse(request);
    }

    public static String postXml(String url, String xml) {
        RequestBody requestBody = RequestBody.create(XML, xml);
        Request request =
                new Request.Builder().url(url).post(requestBody).addHeader("Connection", "close").build();
        return execute(request);
    }

    public static String postJson(String url, String json) {
        RequestBody body = RequestBody.create(JSON, json);
        Request request =
                new Request.Builder().url(url).addHeader("Connection", "close").post(body).build();
        return execute(request);
    }

    public static String put(String url) {
        Request request = new Request.Builder().url(url).addHeader("Connection", "close").build();
        return execute(request);
    }

    public static String putJson(String url, String json) {
        RequestBody body = RequestBody.create(JSON, json);
        Request request =
                new Request.Builder().url(url).addHeader("Connection", "close").put(body).build();
        return execute(request);
    }

    private static String execute(Request request) {
        try (Response response = okHttpClient.newCall(request).execute()) {
            log.info("HttpPoolRsp:{}, {}", response.code(), response.message());
            if (response.isSuccessful()) {
                return response.body().string();
            }
            throw new RuntimeException("Http请求状态错误");
        } catch (Exception e) {
            log.error("OkHttp请求异常", e);
            throw new RuntimeException(String.format("OkHttp请求异常:%s", e.getMessage()));
        }
    }

    private static Response executeResponse(Request request) {

        try (Response response = okHttpClient.newCall(request).execute()) {
            log.info("HttpPoolRsp:{}, {}", response.code(), response.message());
            return response;
        } catch (Exception e) {
            log.error("OkHttp请求异常", e);
            return null;
        }
    }

}
